{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "61a0f50a-318c-4a8b-98e9-17c3bc14b839",
   "metadata": {},
   "source": [
    "## Working with GeoJSON"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc6d7b2d-deb2-4da2-aa31-420c66b63edf",
   "metadata": {},
   "source": [
    "In the 3.0 release of pandapower, significant changes have been made to how geospatial data is stored in the net object. Previously, geospatial data was stored in the *line_geodata* and *bus_geodata* tables. However, this structure caused several problems:\n",
    "\n",
    "- In what projection are the data points stored?\n",
    "- What do x and y represent? Does x represent latitude, longitude or something else?\n",
    "- Copmatibility issues with frontend services and QGIS\n",
    "\n",
    "To resolve this ambiguity, all geospatial data has now been moved to the line and bus tables under the *geo* column. The geometries are stored as GeoJSON objects. [GeoJSON](https://datatracker.ietf.org/doc/html/rfc7946) is a widely used standard for geospatial data, supporting points, lines, and polygons in a unified format with explicit projection definitions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fe634b5e-44ca-43f3-befb-1aecbe995a56",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:11.826245Z",
     "start_time": "2025-10-20T13:21:06.957765Z"
    }
   },
   "outputs": [],
   "source": [
    "from pandapower.networks import mv_oberrhein\n",
    "import pandas as pd\n",
    "pd.set_option(\"display.max_colwidth\", 120)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2bed640f-1894-4b71-9b93-8899dd047f6c",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:16.047612Z",
     "start_time": "2025-10-20T13:21:11.861249Z"
    }
   },
   "outputs": [],
   "source": [
    "net = mv_oberrhein()\n",
    "print(net.line.geo.head(), \"\\n\")\n",
    "print(net.bus.geo.head())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74af88da-10fe-43dd-a8ce-6b3ec57ab9ad",
   "metadata": {},
   "source": [
    "To make working with these new geospatial data easier and more efficient, a pandas series accessor has been introduced. This accessor allows direct interaction with GeoJSON data and integrates functions provided by GeoSeries in geopandas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ee923be1-9fa4-4141-8099-9c2d96b4a169",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:16.109270Z",
     "start_time": "2025-10-20T13:21:16.099272Z"
    }
   },
   "outputs": [],
   "source": [
    "from shapely.geometry import Point\n",
    "from geopandas import GeoSeries\n",
    "from pandapower.plotting import simple_plotly, create_line_trace, create_bus_trace\n",
    "import plotly.graph_objects as go"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b18429f-ffae-481b-bbcf-278aeed2fd1e",
   "metadata": {},
   "source": [
    "Create a reference point and a polygon to check what elements are inside a certain radius. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8de3ac01-8008-42cc-b669-23cf029424e5",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:16.336550Z",
     "start_time": "2025-10-20T13:21:16.170340Z"
    }
   },
   "outputs": [],
   "source": [
    "reference_point = (7.781067, 48.389774)\n",
    "radius_m = 2200\n",
    "circle_polygon = GeoSeries([Point(reference_point)], crs=4326).to_crs(epsg=31467).buffer(radius_m).to_crs(epsg=4326).iloc[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cea9b32-fe45-4c95-8842-a7e688c82e2f",
   "metadata": {},
   "source": [
    "Access the GeoJSON accessor and the needed GeoSeries methods."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f1052155-c72c-4ec2-86ec-9b871c2de3f6",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:16.446552Z",
     "start_time": "2025-10-20T13:21:16.389970Z"
    }
   },
   "outputs": [],
   "source": [
    "lines_intersect = net.line[net.line.geo.geojson.intersects(circle_polygon)].index\n",
    "buses_within = net.bus[net.bus.geo.geojson.within(circle_polygon)].index\n",
    "net.line.geo.geojson.total_bounds"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7966a23-87e9-4dae-a256-c0170234e4fb",
   "metadata": {},
   "source": [
    "And plot the results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32231088-02e8-497d-9cae-279aba140370",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:19.033689Z",
     "start_time": "2025-10-20T13:21:16.500083Z"
    }
   },
   "outputs": [],
   "source": [
    "x, y = circle_polygon.exterior.xy\n",
    "polygon_trace = go.Scatter(\n",
    "    x=list(x), \n",
    "    y=list(y), \n",
    "    fill=\"toself\",\n",
    "    mode=\"lines\",\n",
    "    line={\"color\": \"orange\", \"width\": 0.5},\n",
    "    fillcolor=\"rgba(255, 165, 0, 0.2)\",\n",
    "    name=\"radius\"\n",
    ")\n",
    "\n",
    "point_trace = go.Scatter(\n",
    "    x=[circle_polygon.centroid.x], \n",
    "    y=[circle_polygon.centroid.y], \n",
    "    mode=\"markers\",\n",
    "    marker={\"color\": \"orange\", \"size\": 10},\n",
    "    name=\"reference point\"\n",
    ")\n",
    "lt = create_line_trace(net, lines=lines_intersect, color=\"red\", trace_name='intersecting lines')\n",
    "bt = create_bus_trace(net, buses=buses_within, color=\"red\", trace_name=\"buses within radius\")\n",
    "fig = simple_plotly(net, auto_open=False, additional_traces=lt)\n",
    "fig.add_trace(polygon_trace)\n",
    "fig.add_trace(point_trace)\n",
    "fig.add_trace(bt[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e7917d4-c696-459c-aa59-df11bb2ffac7",
   "metadata": {},
   "source": [
    "It is also possible to load the GeoJSON, or view the geo column as GeoSeries or a Series with shapely objects."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3bfe1268-5b40-44c6-b194-aced9ee9d07d",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:19.112482Z",
     "start_time": "2025-10-20T13:21:19.087478Z"
    }
   },
   "outputs": [],
   "source": [
    "net.bus.geo.head() # entries are strings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4f49408-5d9b-4083-893b-e857c999befd",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:19.342869Z",
     "start_time": "2025-10-20T13:21:19.296171Z"
    }
   },
   "outputs": [],
   "source": [
    "net.bus.geo.geojson.as_geo_obj.head() # entries are dicts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dfbcd7ff-aa5d-4d8f-aaaf-3909dd4fd654",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:20.474282Z",
     "start_time": "2025-10-20T13:21:20.458754Z"
    }
   },
   "outputs": [],
   "source": [
    "net.bus.geo.geojson.as_geoseries.head() # GeoSeries "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc2c3aba-9897-47a3-b076-0fca36134ad2",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:22.330669Z",
     "start_time": "2025-10-20T13:21:22.312673Z"
    }
   },
   "outputs": [],
   "source": [
    "net.bus.geo.geojson.as_shapely_obj.head() # pandas Series with shapely objects"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9240803c-ce51-4086-af42-a79192c8fcd7",
   "metadata": {},
   "source": [
    "It is also possible to get the coordinates or the geometry type from the GeoJSON. \n",
    "**Note that it is not recommended to use ._coords in applications, because the projection definition is lost!**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be589243-c803-48e5-a6ee-ab1140ca350e",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:24.008902Z",
     "start_time": "2025-10-20T13:21:23.983173Z"
    }
   },
   "outputs": [],
   "source": [
    "net.bus.geo.geojson._coords.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "749ec31d-cde0-4d21-b2d9-fe5dd424e5a5",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T13:21:25.856931Z",
     "start_time": "2025-10-20T13:21:25.823167Z"
    }
   },
   "outputs": [],
   "source": [
    "net.bus.geo.geojson.type.head()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
